﻿using System;
using System.Linq;
using System.Web.Mvc;
using CookComputing.XmlRpc;
using System.Collections.Generic;
using System.IO;
using Ninject;
using WeBlog.DataContext;
using WeBlog.Helpers;
using WeBlog.Interfaces;
using WeBlog.Models;
using WeBlog.Repositories;

//50% of this code came from Keyvan Nayyeri's excellent blog post. 
// http://nayyeri.net/implement-metaweblog-api-in-asp-net. He rocks. Nice job.
// The XmlRpcService is the timeless work of Charles Cook. Also rocks and has for a long time.

namespace WeBlog.Services.MetaWeblog
{
    public class MetaWeblog : XmlRpcService, IMetaWeblog
    {
        private readonly SiteConfiguration _siteConfiguration;        

        private readonly PostRepository _postRepository;
        private readonly UserRepository _userRepository;
        private readonly CategoryRepository _categoryRepository;
        private readonly TagRepository _tagRepository;
        private readonly IUnitOfWork _unitOfWork;
      

        public MetaWeblog() 
        {
            var kernel = new StandardKernel();
            _unitOfWork = kernel.Get<WeBlogContext>();
            _siteConfiguration = SiteManager.CurrentConfiguration;

            _postRepository = new PostRepository(_unitOfWork);
            _userRepository = new UserRepository(_unitOfWork);
            _categoryRepository = new CategoryRepository(_unitOfWork);
            _tagRepository = new TagRepository(_unitOfWork);
        }

        private Uri Makelink( string controller, string action, string id )
        {
            Uri baseUri = new Uri(System.Web.HttpContext.Current.Request.Url.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped));
            if( String.IsNullOrWhiteSpace(action) )
                return new Uri(string.Format("{0}/{1}/{2}", baseUri, controller, id ));
            else
                return new Uri(string.Format("{0}/{1}/{2}/{3}", baseUri, controller, action, id));
        }

        private User FindByEmail( string email )
        {
            return _userRepository.FindByEmail(email);
        }        

        public Author[] GetAuthors(string blogid, string username, string password)
        {
            if (ValidateUser(username, password))
            {
                User user = FindByEmail(username);
                var authors = _userRepository.FindAuthors();

                if (user.IsInRole(Role.Contributor) || user.IsInRole(Role.Author))
                    authors = authors.Where(x => x.Email == username);
                
                return (from x in authors
                        select new Author
                        {
                            user_id = x.UserId.ToString(),
                            user_login = x.Email,
                            display_name = x.FullName,
                            user_email = x.Email,
                            meta_value = ""
                        }).ToArray();
            }
            throw new XmlRpcFaultException(0, "User is not valid!");                      
        } 

        public Tag[] GetTags(string blogid, string username, string password)
        {            
            if (ValidateUser(username, password))
            {                
                var tags = _tagRepository.FindAll().OrderBy(x => x.Name).ToList();

                var projection = from x in tags
                                 select new Tag
                                 {
                                     name = x.Name,
                                     count = x.Posts.Count,
                                     tag_id = 1,    
                                     slug = x.Name,
                                     html_url = Makelink("Tag", String.Empty, x.Name).PathAndQuery,
                                     rss_url = Makelink("Tag", String.Empty, x.Name).PathAndQuery
                                 };
                return projection.ToArray();        
            }
            throw new XmlRpcFaultException(0, "User is not valid!");
        }

        void UpdateCategories(WeBlog.Models.Post post, string[] categories)
        {            
            if( post.Categories != null )
                post.Categories.Clear();
   
            if (categories.Length == 0 ) return;

            foreach (var name in categories)
            {
                var category = _categoryRepository.FindByName(name) ;
                if (category != null)                    
                    post.Categories.Add(category);
            }
        }

        private void UpdateTags(WeBlog.Models.Post post, string tagsString)
        {            
            if( post.Tags != null )
                post.Tags.Clear();

            // Remove white space
            tagsString = tagsString.Trim();

            // Need to use a more robust expression where (tag1 tag2 'tag3A tag3B') gets 
            // detected and converted to : tag1 tag2 tag3A-tag3B
            string[] tags = System.Text.RegularExpressions.Regex.Split(tagsString, @"\W+");

            foreach (var tag in tags)
            {
                if (string.IsNullOrWhiteSpace(tag)) continue;
                var t = _tagRepository.FindByName(tag);
                if (t == null)
                {
                    t = new WeBlog.Models.Tag{ Name = tag };
                    post.Tags.Add(t);
                }

                post.Tags.Add(t);
            }
        }

        string IMetaWeblog.AddPost(string blogid, string username, string password,
            Post post, bool publish)
        {
            if (ValidateUser(username, password))
            {                
                User user = FindByEmail(username);

                var newPost = new WeBlog.Models.Post();
                newPost.Title = post.title;
                newPost.Content = post.description;
                newPost.LastModified = DateTime.Now;
                newPost.Published = publish;
                newPost.Slug = post.wp_slug;

                if( String.IsNullOrWhiteSpace(post.wp_slug))
                    newPost.Slug = Utils.RemoveIllegalCharacters(newPost.Title);

                newPost.AreCommentsEnabled = _siteConfiguration.AreCommentsEnabled;
                
                if (newPost.Published)
                    newPost.PublishDate = DateTime.Now;

                newPost.Author = user;
                _postRepository.Add(newPost);

                UpdateCategories(newPost, post.categories);
                UpdateTags(newPost, post.mt_keywords);
                
                _unitOfWork.Commit();
               
                return newPost.PostId.ToString();                               
            }
            throw new XmlRpcFaultException(0, "User is not valid!");
        }

        bool IMetaWeblog.UpdatePost(string postid, string username, string password,
            Post post, bool publish)
        {
            if (ValidateUser(username, password))
            {
                User user = FindByEmail(username);

                int id = Int32.Parse( postid );
                var p = _postRepository.Find(id);                
                p.Title = post.title;
                p.Content = post.description;
                p.LastModified = DateTime.Now;
                p.Published = publish;
                p.Slug = post.wp_slug;                

                if (p.Published)
                    p.PublishDate = DateTime.Now;

                p.Author = user;
                UpdateCategories(p, post.categories);
                UpdateTags(p, post.mt_keywords);
                _unitOfWork.Commit(); 

                return true;
            }
            throw new XmlRpcFaultException(0, "User is not valid!");
        }

        Post IMetaWeblog.GetPost(string postid, string username, string password)
        {
            if (ValidateUser(username, password))
            {
                var id = Int32.Parse( postid );
                var post = _postRepository.Find(id);

                return new Post
                {
                    title = post.Title,
                    categories = post.Categories.Select(c => c.Name).ToArray(),
                    dateCreated = post.PublishDate ?? DateTime.Now,
                    description = post.Content,
                    userid = post.Author.ToString(),
                    postid = post.PostId.ToString(),
                    wp_slug = post.Slug,
                    permalink = post.GetPermalink().PathAndQuery
                };                
            }
            throw new XmlRpcFaultException(0, "User is not valid!");
        }

        CategoryInfo[] IMetaWeblog.GetCategories(string blogid, string username, string password)
        {
            if (ValidateUser(username, password))
            {
                var categories = _categoryRepository.FindAll().ToList();

                var projection = from x in categories 
                                 select new CategoryInfo
                                 {
                                    categoryid = x.CategoryId.ToString(),
                                    description = x.Description,
                                    title = x.Name,
                                    htmlUrl = Makelink( "Category", String.Empty, x.Name ).PathAndQuery,
                                    rssUrl = Makelink( "Category", String.Empty, x.Name ).PathAndQuery
                                 };
                return projection.ToArray();                
            }
            throw new XmlRpcFaultException(0, "User is not valid!");
        }

        Post[] IMetaWeblog.GetRecentPosts(string blogid, string username, string password, int numberOfPosts)
        {
            if (ValidateUser(username, password))
            {
                var recent = _postRepository.FindAll()
                                .Where( x => x.PublishDate.HasValue );

                var user = FindByEmail(username);
                if( !user.IsInRole(Role.Admin) || !user.IsInRole(Role.Editor) ) {
                    recent = recent.Where(x => x.Author.Email == username);
                }

                var posts = from x in recent.OrderBy(x => x.PublishDate).Take(numberOfPosts).ToList() 
                            select new Post
                            {
                                title = x.Title,
                                categories = x.Categories.Select( c => c.Name ).ToArray(),
                                dateCreated = x.PublishDate ?? DateTime.Now,
                                description = x.Content,
                                userid = x.Author.ToString(),
                                postid = x.PostId.ToString(),
                                wp_slug = x.Slug,
                                permalink = x.GetPermalink().PathAndQuery
                            };

                return posts.ToArray();
            }
            throw new XmlRpcFaultException(0, "User is not valid!");
        }

        MediaObjectInfo IMetaWeblog.NewMediaObject(string blogid, string username, string password,
            MediaObject mediaObject)
        {
            if (ValidateUser(username, password))
            {
                MediaObjectInfo objectInfo = new MediaObjectInfo();

                string path = mediaObject.name.Replace("/", "\\").Replace("WindowsLiveWriter\\", "");
                
                FileInfo file;

                if (FileManager.IsImage(path)) {
                    file = FileManager.GetUniqueFile(FileManager.ImageDirectory, new FileInfo(path));
                    objectInfo.url = "/Images/" + file.Name;
                }
                else {
                    file = FileManager.GetUniqueFile(FileManager.FileDirectory, new FileInfo(path));
                    objectInfo.url = "/Files/" + file.Name;
                }

                File.WriteAllBytes(file.FullName, mediaObject.bits);
                return objectInfo;
            }
            throw new XmlRpcFaultException(0, "User is not valid!");
        }

        bool IMetaWeblog.DeletePost(string key, string postid, string username, string password, bool publish)
        {
            if (ValidateUser(username, password))
            {
                var id = Int32.Parse(postid);
                var post = _postRepository.Find(id );
                _postRepository.Remove(post);
                _unitOfWork.Commit();
            }
            throw new XmlRpcFaultException(0, "User is not valid!");
        }

        BlogInfo[] IMetaWeblog.GetUsersBlogs(string key, string username, string password)
        {
            if (ValidateUser(username, password))
            {
                List<BlogInfo> infoList = new List<BlogInfo>();
                BlogInfo b = new BlogInfo();
                b.blogName = _siteConfiguration.Name;
                b.blogid = _siteConfiguration.Name;
                b.url = _siteConfiguration.FindHostHeaders().Any() ? _siteConfiguration.FindHostHeaders().First() : SiteManager.GetBaseUrl();
                infoList.Add(b);
                return infoList.ToArray();
            }
            throw new XmlRpcFaultException(0, "User is not valid!");
        }

        UserInfo IMetaWeblog.GetUserInfo(string key, string username, string password)
        {
            if (ValidateUser(username, password))
            {
                UserInfo info = new UserInfo();
                var user = FindByEmail(key);
                info.email = user.Email;
                info.firstname = user.FirstName;
                info.lastname = user.LastName;
                info.nickname = user.Email;
                info.url = Makelink("Account", "Details", user.UserId.ToString()).PathAndQuery;
                info.userid = user.UserId.ToString();
                return info;
            }
            throw new XmlRpcFaultException(0, "User is not valid!");
        }

        private bool ValidateUser(string username, string password)
        {
            User user = FindByEmail(username);
            if (user == null ) return false;

            if (user.IsInRole(Role.Subscriber))
                return false;
            
            return user.Validate(password);
        }

        
    }
}
